package com.dk.controller;


import com.dk.bean.Result;
import com.dk.em.ResultEnum;
import com.dk.greand.ShiroUtils;
import com.dk.pojo.CarUser;
import com.dk.pojo.EmailModel;
import com.dk.service.CarUserService;
import com.dk.util.MailSendUtils;
import com.dk.util.RedisPermissionConstant;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Reference;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author 张成涛
 * @since 2021-08-11
 */
@RestController
@RequestMapping("carUser")
public class CarUserController {

    @Autowired
    private CarUserService carUserService;
    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private MailSendUtils mailSendUtils;

    //通过用户名去查询 用户是否存在
    @ApiOperation("通过用户名查询")
    @PostMapping("/c")
    @ApiImplicitParam(name = "username", value = "用户账号", required = true, dataTypeClass = String.class, paramType = "path")

    public Result selectByAccount(@RequestParam("username") String username) {
        try {
            //通过用户名进行查询
            CarUser username1 = carUserService.findTbUserByName(username);
            //校验用户是否为空
            if (username1 != null) {
                return Result.buildResult(ResultEnum.SUCCESS);
            }
            return Result.buildResult(ResultEnum.ERROR);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.buildResult(ResultEnum.SUCCESS);
        }
    }

    //注册
    @ApiOperation("注册用户接口")
    @PostMapping("/q")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "carUser", value = "用户实体类", required = true, dataTypeClass = CarUser.class, paramType = "body")


    })
    public Result insertUser(@RequestBody CarUser carUser) {
        try {
            //通过用户名进行查询
            CarUser tbUserByName = carUserService.findTbUserByName(carUser.getUsername());
            //校验用户名是否存在
            if (tbUserByName == null) {
                //校验 前台 验证码 和后台验证码是否一致
                //MD5盐值加密
                //1. 获取随机的盐
                String s = ShiroUtils.generateSalt(7);
                // encryptPassword 参数 1. 加密的类型  2. 铭文的密码  3. 盐  4. 加密叠加的次数
                String md5 = ShiroUtils.encryptPassword("MD5", carUser.getPassword(), s, 4);
                carUser.setSalt(s);
                carUser.setPassword(md5);
                //调用增加方法
                EmailModel emailModel = new EmailModel();
                emailModel.setRecieverName("涛");
                emailModel.setRecieverEmailAddress(carUser.getEmail());
                emailModel.setEmailTheme("测试");
                emailModel.setEmailContent("What are you  弄啥嘞?");
                mailSendUtils.sendEmailAsSysExceptionHtml(emailModel);
                carUserService.insetUser(carUser);
            }
            return Result.buildResult(ResultEnum.SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.buildResult(ResultEnum.ERROR);
        }
    }

    @GetMapping
    @ApiOperation(value = "用户登录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户登录名称", required = true, paramType = "query"),
            @ApiImplicitParam(name = "password", value = "用户登录密码", required = true, paramType = "query")
    })
    public Result login(@RequestParam("username") String userName, @RequestParam("password") String password) {

        redisTemplate.setValueSerializer(new StringRedisSerializer());
        Map<String, Object> map = new HashMap<>();
        map.put("code", false);

        //错误次数的key值
        String failKey = RedisPermissionConstant.USERS_LOGIN_COUNT_FAIL + userName;
        //锁定的key
        String lockKey = RedisPermissionConstant.USERS_LOGIN_COUNT_LOCK + userName;

        //1.先判断是否被冻结
        if (redisTemplate.hasKey(lockKey)) {
            map.put("message", "账号被限制,请" + redisTemplate.getExpire(lockKey, TimeUnit.MINUTES) + "分钟后进行再次登录");//getExpire:根据key获取过期时间
            return Result.buildResult(ResultEnum.ERROR).setData(map);
        }

        // 1.获取security对象(安全) auth 登录有关
        Subject subject = SecurityUtils.getSubject();
        //2.将表单传过来的参数,封装成token : 令牌
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        //3.调用subject的login方法执行登录功能  调用 subject.login会自动跳转到自定义的认证方法
        //shiro是以抛异常的方式告诉我们账号不存在或者密码错误
        try {
            subject.login(token);
            //清空登录次数
            redisTemplate.delete(failKey);
            map.put("code", true);
            return Result.buildResult(ResultEnum.SUCCESS).setData(map);
        } catch (UnknownAccountException e) {
            System.out.println("账号不存在");
            map.put("message", "账号不存在");
            return Result.buildResult(ResultEnum.ERROR).setData(map);
        } catch (IncorrectCredentialsException e) {
            //判断是否为第一次密码错误
            if (!redisTemplate.hasKey(failKey)) {//判断key是否存在//不存在
                redisTemplate.opsForValue().set(failKey, "1");
                redisTemplate.expire(failKey, 2, TimeUnit.MINUTES);//expire:指定key的失效时间
                map.put("message", "在2分钟之内,还可以输入密码4次");
                return Result.buildResult(ResultEnum.ERROR).setData(map);
            } else {
                int count = Integer.parseInt(redisTemplate.opsForValue().get(failKey).toString());
                if (count < 4) {
                    redisTemplate.opsForValue().increment(failKey, 1);
                    Long seconds = redisTemplate.getExpire(failKey, TimeUnit.SECONDS);//根据key获取过期时间
                    map.put("message", "在" + seconds + "秒之内,还可以输入密码" + (4 - count) + "次");
                    return Result.buildResult(ResultEnum.ERROR).setData(map);
                } else {
                    redisTemplate.opsForValue().set(lockKey, "lock");
                    redisTemplate.expire(lockKey, 1, TimeUnit.HOURS);
                    map.put("message", "在1小时之内,不可以登录");
                    return Result.buildResult(ResultEnum.ERROR).setData(map);
                }
            }
        }
    }
}

